package com.workflow.sdk.infra.repository.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.workflow.sdk.core.WorkflowExecutorResult;
import com.workflow.sdk.infra.common.ErrorMessage;
import com.workflow.sdk.infra.util.NumberUtil;
import com.workflow.sdk.infra.repository.mapper.WorkflowExecuteMapper;
import com.workflow.sdk.infra.repository.model.WorkflowExecutePo;
import com.workflow.sdk.infra.repository.WorkflowExecuteRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * <p>
 * 流程执行表 服务实现类
 * </p>
 *
 * @author cyj
 * @since 2022-06-01
 */
@Service
@Slf4j
public class WorkflowExecuteRepositoryImpl extends ServiceImpl<WorkflowExecuteMapper, WorkflowExecutePo> implements WorkflowExecuteRepository {

    @Override
    public List<WorkflowExecutePo> getWorkflowExecute(Long workflowExecuteId) {

        LambdaQueryWrapper<WorkflowExecutePo> lambdaQueryWrapper = Wrappers.lambdaQuery();
        lambdaQueryWrapper.eq(WorkflowExecutePo::getExecuteId, workflowExecuteId);
        return this.list(lambdaQueryWrapper);
    }

    @Override
    public List<WorkflowExecutePo> getExecuteIdByRe(String relevance) {

        if (StringUtils.isEmpty(relevance)) {
            return new ArrayList<>();
        }

        LambdaQueryWrapper<WorkflowExecutePo> lambdaQueryWrapper = Wrappers.lambdaQuery();
        lambdaQueryWrapper.eq(WorkflowExecutePo::getRelevance, relevance);
        return this.list(lambdaQueryWrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeWorkflowExecute(String nextNode, WorkflowExecutePo activeNode, List<WorkflowExecutePo> workflowExecutePos, String remark) {

        // choose对象必须有nextNode
        if (activeNode.getNodeType().equals(WorkflowExecutePo.Type.choose.name())
                && StringUtils.isEmpty(nextNode)) {
            throw new RuntimeException(ErrorMessage.ERROR2);
        }

        // 更新本节点
        this.complete(activeNode, remark);

        // 如果强制结束则直接结束（如驳回这类）
        if (WorkflowExecutorResult.WORKFLOW_END.equals(nextNode)) {
            // 直接结束流程
            WorkflowExecutePo nextWorkflowExecutePo = workflowExecutePos.stream().filter(workflowExecutePo ->
                    workflowExecutePo.getNodeType().equals(String.valueOf(WorkflowExecutePo.Type.end))).
                    findFirst().orElse(null);
            this.complete(nextWorkflowExecutePo, null);
            log.info("流程已完结：executeId:{}, workflowId:{}", activeNode.getExecuteId(), activeNode.getWorkflowId());
            return;
        }

        // 更新后继节点
        // 正常流传
        String childrenNode = activeNode.getChildrenNode();
        if (null != nextNode) {
            // 选择后的流程
            childrenNode = nextNode;
        }
        // 正常流转下一个流程
        if (!NumberUtil.isPositiveInt(childrenNode)) {
            throw new RuntimeException(ErrorMessage.ERROR1);
        }
        Integer children = Integer.valueOf(childrenNode);
        WorkflowExecutePo nextWorkflowExecutePo = workflowExecutePos.stream().filter(workflowExecutePo -> workflowExecutePo.getNode().equals(children)).findFirst().orElse(null);
        if (null == nextWorkflowExecutePo) {
            throw new RuntimeException(String.format(ErrorMessage.ERROR3, children));
        }
        // 如果是终结节点则完成
        if (nextWorkflowExecutePo.getNodeType().equals(String.valueOf(WorkflowExecutePo.Type.end))) {
            this.complete(nextWorkflowExecutePo, null);
            log.info("流程已完结：executeId:{}, workflowId:{}", activeNode.getExecuteId(), activeNode.getWorkflowId());
        } else {
            // 不是终结节点则设置状态
            nextWorkflowExecutePo.setStatus(WorkflowExecutePo.Status.DOING.code());
            this.updateById(nextWorkflowExecutePo);
        }
    }

    private void complete(WorkflowExecutePo activeNode, String remark) {
        activeNode.setStatus(WorkflowExecutePo.Status.COMPLATE.code());
        activeNode.setExecuteTime(new Date());
        activeNode.setRemark(remark);
        this.updateById(activeNode);
    }


}
